//
// Created by augtons on 2022/1/7.
//

#include "my_udp.h"

#define BETWEEN0_255(v) ((v) <= 255)
#define VALID_IPV4(a, b, c, d)  (BETWEEN0_255(a) && BETWEEN0_255(b) && BETWEEN0_255(c) && BETWEEN0_255(d))

static void computerCmdCalled(const char *data);
static void phoneCmdCalled(const char *data);
int myudp_create_socket(struct sockaddr *pserviceName);

void initUDPTaskManager(){
    memset(&serviceName_computer, 0, sizeof(serviceName_computer));
    memset(&serviceName_phone, 0, sizeof(serviceName_phone));

    serviceName_computer.sin_addr.s_addr = INADDR_ANY;
    serviceName_computer.sin_family = AF_INET;
    serviceName_computer.sin_port = htons(WINDSWING_PORT_COMPUTER);

    serviceName_phone.sin_addr.s_addr = INADDR_ANY;
    serviceName_phone.sin_family = AF_INET;
    serviceName_phone.sin_port = htons(WINDSWING_PORT_PHONE);

    memset(&computer_udp_manager, 0, sizeof(computer_udp_manager));
    memset(&phone_udp_manager, 0, sizeof(phone_udp_manager));

    computer_udp_manager.create = task_computer_create;
    computer_udp_manager.destroy = task_computer_destroy;

    phone_udp_manager.create = task_phone_create;
    phone_udp_manager.destroy = task_phone_destroy;

    phone_message_queue = xQueueCreate(1, sizeof(PhoneMessage));
}

void initUDPSockets(){
    computer_udp_manager.sock = myudp_create_socket((struct sockaddr*)&serviceName_computer);  //might block
    phone_udp_manager.sock = myudp_create_socket((struct sockaddr*)&serviceName_phone);     //might block
}

static bool setRemoteIP(const char *data, struct sockaddr_in *dest){
    uint32_t a=0, b=0, c=0, d=0;
    uint32_t port=0;
    char ip[16] = {0};

    ESP_LOGI("Remote_Setting", "IP:PORT-> %s", data);
    if(5 == sscanf(data, "%d.%d.%d.%d:%d", &a, &b, &c, &d, &port)){
        if(VALID_IPV4(a, b, c, d)){
            memset(dest, 0, sizeof(struct sockaddr_in));
            sprintf(ip, "%d.%d.%d.%d", a, b, c, d);
            dest->sin_family = AF_INET;
            dest->sin_port = htons(port);
            dest->sin_addr.s_addr = inet_addr(ip);
            return true;
        }
    }
    ESP_LOGI("Remote_Setting", "Invalid IP");
    return false;
}

int myudp_create_socket(struct sockaddr *pserviceName){
    int sock, err;
    while (1) {
        sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
        if (sock < 0) {
            continue;
        }
        err = bind(sock, pserviceName, sizeof(struct sockaddr_in));
        if (err != 0) {
            closesocket(sock);
            continue;
        }
        return sock;
    }
}

/***********task_computer************/
void task_computer_create(){
    if(computer_udp_manager.recv_handle == NULL) {
        xTaskCreate(task_computer_recv, "computer", COMPUTER_RECV_TASK_S, NULL, COMPUTER_RECV_TSAK_P,
                    computer_udp_manager.recv_handle);
    }
    if(computer_udp_manager.send_handle == NULL) {
        xTaskCreate(task_computer_send, "cmptr_send", COMPUTER_SEND_TASK_S, NULL, COMPUTER_SEND_TASK_P,
                    &computer_udp_manager.send_handle);
    }
}

void task_computer_destroy(){
    xEventGroupClearBits(wifiEventGroup, GOT_COMPUTER_IP_BIT);
    xEventGroupClearBits(wifiEventGroup, UP_MACHINE_NEEDDATA_BIT);
    if(computer_udp_manager.recv_handle != NULL){
        vTaskDelete(computer_udp_manager.recv_handle);
        computer_udp_manager.recv_handle = NULL;
    }
    if(computer_udp_manager.send_handle != NULL){
        vTaskDelete(computer_udp_manager.send_handle);
        computer_udp_manager.send_handle = NULL;
    }
}

_Noreturn void task_computer_recv(void *args){
    struct sockaddr_in from;
    socklen_t fromlen;
    char recvValue[24];
    while (1){
        bzero(recvValue, 24);
        recvfrom(computer_udp_manager.sock, recvValue, sizeof(recvValue), 0, (struct sockaddr*)&from, &fromlen);
        computerCmdCalled(recvValue);
    }
}

_Noreturn void task_computer_send(void *args){
//    int led = 0;
    int ret = 0;
    char data[32];
    while (1){
        xEventGroupWaitBits(wifiEventGroup,
                            GOT_COMPUTER_IP_BIT | UP_MACHINE_NEEDDATA_BIT,
                            pdFALSE, pdTRUE, portMAX_DELAY);

        sprintf(data, "%f:%f", get_tileAngle(), get_azimuth());

        ret = sendto(computer_udp_manager.sock, data, 5, 0, (struct sockaddr*)&computerName, sizeof(computerName));
        ESP_LOGI("SEND_TO_COMPUTER", "ret = %d, errno = %d", ret, errno);
        vTaskDelay(pdMS_TO_TICKS(250));
//
//        gpio_set_level(GPIO_NUM_2, led);
//        led = ~led;
    }
}



/***********task_phone************/
void task_phone_create(){
    if(phone_udp_manager.recv_handle == NULL) {
        xTaskCreate(task_phone_recv, "phone", PHONE_RECV_TASK_S, NULL, PHONE_RECV_TASK_P,
                    &phone_udp_manager.recv_handle);
    }
    if(phone_udp_manager.send_handle == NULL) {
        xTaskCreate(task_phone_send, "phn_send", PHONE_SEND_TASK_S, NULL, PHONE_SEND_TASK_P,
                    &phone_udp_manager.send_handle);
    }
}

void task_phone_destroy(){
    xEventGroupClearBits(wifiEventGroup, GOT_PHONE_IP_BIT);
//    xEventGroupClearBits(wifiEventGroup, PHONE_DATA_BIT);
    if(phone_udp_manager.recv_handle != NULL){
        vTaskDelete(phone_udp_manager.recv_handle);
        phone_udp_manager.recv_handle = NULL;
    }
    if(phone_udp_manager.send_handle != NULL){
        vTaskDelete(phone_udp_manager.send_handle);
        phone_udp_manager.send_handle = NULL;
    }
}

_Noreturn void task_phone_recv(void *args){
    struct sockaddr_in from;
    socklen_t fromlen;
    char recvValue[24];
    while (1){
        bzero(recvValue, 24);
        recvfrom(phone_udp_manager.sock, recvValue, sizeof(recvValue), 0, (struct sockaddr*)&from, &fromlen);
        phoneCmdCalled(recvValue);
    }
}

_Noreturn void task_phone_send(void *args){
    int ret = 0;
    PhoneMessage phoneMessage;
    while (1){
        xQueueReceive(phone_message_queue, &phoneMessage, portMAX_DELAY);
        EventBits_t bits = xEventGroupWaitBits(wifiEventGroup,GOT_PHONE_IP_BIT,
                            pdFALSE, pdTRUE, 0);
        if(bits & GOT_PHONE_IP_BIT) {
            ret = sendto(phone_udp_manager.sock, phoneMessage.msg, strlen(phoneMessage.msg), 0, (struct sockaddr *) &phoneName,
                         sizeof(phoneName));
            ESP_LOGI("SEND_TO_PHONE", "ret = %d, errno = %d", ret, errno);
        }
    }
}


/***********cmd chu li************/
static void computerCmdCalled(const char *data){
switch (data[0]) {
    case 'A': { //set addr
        data += 1;
        if(setRemoteIP(data, &computerName)){
            ESP_LOGI("Remote_Setting", "OK!(computer ip)");
            xEventGroupSetBits(wifiEventGroup, GOT_COMPUTER_IP_BIT);
            xEventGroupClearBits(wifiEventGroup, UP_MACHINE_NEEDDATA_BIT);
        }
        break;
    }
    case 'B': { //set want data or not
        ESP_LOGI("COMPUTER", "B command");
        if (data[1] == 'A'){   // BA(CK)
            xEventGroupSetBits(wifiEventGroup, UP_MACHINE_NEEDDATA_BIT);
        }else if(data[1] == 'N') {    //BN(ACK)
            xEventGroupClearBits(wifiEventGroup, UP_MACHINE_NEEDDATA_BIT);
        }
        break;
    }
    case 'C': {
        data += 1;
        windswing_c_command(data);
        break;
    }
    case 'D': {
        data += 1;
        windswing_d_command(data);
        break;
    }
}
}

static void phoneCmdCalled(const char *data){
    PhoneMessage okMsg = {
        .msg = "ROK!"
    };
switch (data[0]) {
    case 'A': { //set addr
        data += 1;
        if(setRemoteIP(data, &phoneName)){
            ESP_LOGI("Remote_Setting", "OK!(phone ip)");
            xEventGroupSetBits(wifiEventGroup, GOT_PHONE_IP_BIT);
            xQueueSend(phone_message_queue, &okMsg, pdMS_TO_TICKS(10));
        }
        break;
    }
    case 'B': {
        xQueueSend(phone_message_queue, &okMsg, pdMS_TO_TICKS(10));
        ESP_LOGI("PHONE", "RECEIVED_B");
        break;
    }
    case 'C': {
        data += 1;
        xQueueSend(phone_message_queue, &okMsg, pdMS_TO_TICKS(10));
        windswing_c_command(data);
        break;
    }
    case 'D': {
        data += 1;
        xQueueSend(phone_message_queue, &okMsg, pdMS_TO_TICKS(10));
        windswing_d_command(data);
        break;
    }
//    case 'E': {
//        PhoneMessage msg = {0};
//        msg.msg[0] = 'H';
//        msg.msg[1] = 'e';
//        msg.msg[2] = 'l';
//        msg.msg[3] = 'l';
//        msg.msg[4] = 'o';
//        xQueueSend(phone_message_queue, &msg, 0);
//    }
}
}